/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                                                                           *
 *                          Copyright (c) 2010                               *
 *                  Alen Stojanov [ ioi.alen@gmail.com ]                     *
 *                Jacobs University Bremen, Bremen, Germany                  *
 *                                                                           *
 *                          All Rights Reserved.                             *
 *                                                                           *
 *    Permission to use, copy, modify and distribute this software and       *
 *    its documentation is hereby granted, provided that both the            *
 *    copyright notice and this permission notice appear in all copies of    *
 *    the software, derivative works or modified versions, and any           *
 *    portions thereof, and that both notices appear in supporting           *
 *    documentation.                                                         *
 *                                                                           *
 *    This code is distributed in the hope that it will be useful, but       *
 *    WITHOUT ANY WARRANTY; without even the implied warranty of             *
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS       *
 *    DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER          *
 *    RESULTING FROM THE USE OF THIS SOFTWARE.                               *
 *                                                                           *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <QtGui>
#include <QtOpenGL>
#include <QColor>
#include <algorithm>

#include "gllinegraph.h"
#include "glpreview.h"
#include "tsdata.h"

using namespace std;

GLLineGraph::GLLineGraph(QWidget *parent)
{
	isInitialized = false;
	highlighedCount = 0;
	gridSize = 5;
	marginSize = 50.0;
}

void GLLineGraph::on_addSeries (QVector<TSData*> series)
{
	/* add the new time series into the vector */
	mutex.lock();
	data += series;
	highlighedData += QVector<bool>(series.size(), false);
	mutex.unlock();

	update();
}

void GLLineGraph::initializeGL()
{
	/* load a white color on the screen */
	qglClearColor(QColor::fromRgb(0, 0, 0));

	/* enable blending */
	glEnable (GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	/* enable line-antialiasing */
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
}

void GLLineGraph::paintGL()
{
	/* clear the projection matrix */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	/* get the viewport */
	QSize viewport = this->size();
	double h = viewport.height();
	double w = viewport.width();

	/* prepare the viewport in case of no data */
	/* and print the default message on the screen */
	if ( data.size() == 0 ) {
		gluOrtho2D(0, w, 0, h);
		glMatrixMode(GL_MODELVIEW);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		glColor3f(1.0f, 1.0f, 1.0f);
		this->renderText( w / 2 - 200, h / 2, 0, "The Main Graph has not been loaded with data");
		glFlush();
	} else if (isInitialized) {
		/* determine the coordinates of the time series data */
		double minX = data.at(0)->getX(data.at(0)->getNearestXIndex(rangeMinX, TSData::NearestCeil));
		double maxX = data.at(0)->getX(data.at(0)->getNearestXIndex(rangeMaxX, TSData::NearestFloor));
		double minY = data.at(0)->getMinY(rangeMinX, rangeMaxX);
		double maxY = data.at(0)->getMaxY(rangeMinX, rangeMaxX);
		for ( int i = 0; i < data.size(); ++i ) {
			minX = std::min(minX, data.at(i)->getX(data.at(i)->getNearestXIndex(rangeMinX, TSData::NearestCeil)));
			maxX = std::max(maxX, data.at(i)->getX(data.at(i)->getNearestXIndex(rangeMaxX, TSData::NearestFloor)));
			minY = std::min(minY, data.at(i)->getMinY(rangeMinX, rangeMaxX));
			maxY = std::max(maxY, data.at(i)->getMaxY(rangeMinX, rangeMaxX));
		}

		/* calculate the margins */
		double marginSizeX = marginSize / w * (maxX - minX);
		double marginSizeY = marginSize / h * (maxY - minY);

		double arrowX = marginSizeX / 7;
		double arrowY = marginSizeY / 7;

		double verticalGrid   = (maxY - minY) / gridSize;
		double horizontalGrid = (maxX - minX) / gridSize;

		/* use the ratio on the width only */
		gluOrtho2D(minX - marginSizeX, maxX + marginSizeX, minY - marginSizeY, maxY + marginSizeY);
		glMatrixMode(GL_MODELVIEW);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();

		//
		// Draw the grids
		//
		glColor3f(0.5f, 0.0f, 0.0f);
		glEnable(GL_LINE_STIPPLE);
		glLineStipple(4, 0xAAAA);
		glBegin(GL_LINES);
		for (int i = 0; i <= gridSize; ++i) {
			glVertex3f ( minX - (marginSizeX / 2.0) + arrowX / 2.0, minY + i * verticalGrid, 0.0f );
			glVertex3f ( maxX, minY + i * verticalGrid, 0.0f );
		}
		glEnd();
		glBegin(GL_LINES);
		for (int i = 0; i <= gridSize; ++i) {
			glVertex3f ( minX + i * horizontalGrid, minY - (marginSizeY / 2.0) + arrowY / 2, 0.0f );
			glVertex3f ( minX + i * horizontalGrid, maxY, 0.0f );
		}
		glEnd();
		glDisable(GL_LINE_STIPPLE);

		if ( highlighedCount > 0 ) {
			glColor3f(0.2f, 0.2f, 0.2f);
		} else {
			glColor3f(1.0f, 1.0f, 1.0f);
		}

		/* draw the non-highlighted time-series data */
		for ( int i = 0; i < data.size(); ++i ) {
			if ( highlighedData[i] ) {
				continue;
			}
			TSData& tsdata = *data[i];
			int start 	= tsdata.getNearestXIndex(rangeMinX, TSData::NearestCeil);
			int end 	= tsdata.getNearestXIndex(rangeMaxX, TSData::NearestFloor);
			for ( int j = start + 1; j <= end; ++j ) {
				glBegin(GL_LINES);
				glVertex3f ( tsdata.getX(j-1), tsdata.getY(j-1), 0.0f );
				glVertex3f ( tsdata.getX(j)  , tsdata.getY(j)  , 0.0f );
				glEnd();
				glLineWidth(1.0f);
			}
		}

		/* draw the highlighted time-series data */
		for ( int i = 0; i < data.size(); ++i ) {
			if ( !highlighedData[i] ) {
				continue;
			}
			TSData& tsdata = *data[i];
			int start 	= tsdata.getNearestXIndex(rangeMinX, TSData::NearestCeil);
			int end 	= tsdata.getNearestXIndex(rangeMaxX, TSData::NearestFloor);
			QColor lineColor = tsdata.getColor();
			glColor3f(lineColor.redF(), lineColor.greenF(), lineColor.blueF());
			for ( int j = start + 1; j <= end; ++j ) {
				glLineWidth(2.0f);
				glBegin(GL_LINES);
				glVertex3f ( tsdata.getX(j-1), tsdata.getY(j-1), 0.0f );
				glVertex3f ( tsdata.getX(j)  , tsdata.getY(j)  , 0.0f );
				glEnd();
				glLineWidth(1.0f);
			}
		}

		glColor3f(0.5f, 0.0f, 0.0f);
		glLineWidth(2.0f);
		/* draw the axis */
		glBegin(GL_LINES);
			/* draw the vertical y-axis */
			glVertex3f ( minX - (marginSizeX / 2.0), maxY, 0.0f );
			glVertex3f ( minX - (marginSizeX / 2.0), minY, 0.0f );
			/* draw the vertical grid */
			for (int i = 0; i <= gridSize; ++i) {
				glVertex3f ( minX - (marginSizeX / 2.0) - arrowX / 2.0, minY + i * verticalGrid, 0.0f );
				glVertex3f ( minX - (marginSizeX / 2.0) + arrowX / 2.0, minY + i * verticalGrid, 0.0f );
			}
			/* draw the horizontal x-axis */
			glVertex3f ( minX, minY - (marginSizeY / 2.0), 0.0f );
			glVertex3f ( maxX, minY - (marginSizeY / 2.0), 0.0f );
			/* draw the horizontal grid */
			for (int i = 0; i <= gridSize; ++i) {
				glVertex3f ( minX + i * horizontalGrid, minY - (marginSizeY / 2.0) + arrowY / 2, 0.0f );
				glVertex3f ( minX + i * horizontalGrid, minY - (marginSizeY / 2.0) - arrowY / 2, 0.0f );
			}
		glEnd();
		glLineWidth(1.0f);

		for (int i = 0; i <= gridSize; ++i) {
			double textX = (marginSizeX / 2.0) / ( maxX - minX + 2 * marginSize ) * w;
			double textY = (marginSizeY + i * verticalGrid) / ( maxY - minY + 2 * marginSize ) * h;
			// this->renderText(textX, textY, "Nekoj tekst");
			// glVertex3f ( minX - (marginSizeX / 2.0) + arrowX / 2.0, minY + i * verticalGrid, 0.0f );
		}



		glFlush();
	}
}

void GLLineGraph::mousePressEvent(QMouseEvent *event)
{
	// lastPos = event->pos();
}

void GLLineGraph::mouseMoveEvent(QMouseEvent *event)
{

}

void GLLineGraph::on_updateRange (double rangeMinX, double rangeMaxX)
{
	this->rangeMinX	 = rangeMinX;
	this->rangeMaxX	 = rangeMaxX;
	isInitialized 	 = true;
	this->updateGL();
}

void GLLineGraph::resizeGL(int width, int height)
{
	glViewport(0, 0, width, height);
}

void GLLineGraph::on_dataHighlight (QVector<TSData*> series)
{
	for ( int i = 0; i < data.size(); ++i) {
		if ( series.contains(data[i]) ) {
			highlighedData[i] = true;
		} else {
			highlighedData[i] = false;
		}
	}
	highlighedCount = series.size();
	updateGL();
}

GLLineGraph::~GLLineGraph()
{

}
